package la.iok.aone.monitor.youtube.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import la.iok.aone.monitor.youtube.model.YoutubeLink;
import la.iok.aone.monitor.youtube.service.YoutubeLinkService;
import la.iok.aone.monitor.youtube.youtube.Auth;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class CheckYoutubeLink {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * YouTube API KEY
     * 默认值：AIzaSyAptYgyRCz6mTGvzWtmBbUQcsOOFkIFhJU
     */
    @Value("${config.monitor.task.youtube.api.key:AIzaSyAptYgyRCz6mTGvzWtmBbUQcsOOFkIFhJU}")
    private String youtubeApiKey;
    /**
     * YouTube API 获取视频信息的哪些部分信息
     * 默认值：snippet,contentDetails,statistics
     */
    @Value("${config.monitor.task.youtube.api.video.part:contentDetails}")
    private String youtubeVideoPart;
    /**
     * 监控链接获取接口服务器地址
     * 默认值：http://web4.long.tv
     */
    @Value("${config.sync.api.server:http://web4.long.tv}")
    private String syncApiServer;
    /**
     * 监控链接结果上报接口API地址
     * 默认值：/index.php?route=longtv/polling_check_link/feedback_result
     */
    @Value("${config.sync.api.interface.feedback:/index.php?route=longtv/polling_check_link/feedback_result}")
    private String syncInterfaceFeedback;
    /**
     * 监控链接结果上报接口参数名：链接编号列表
     * 默认值：product_ids
     */
    @Value("${config.sync.api.interface.feedback.params.key.ids:product_ids}")
    private String feedbackParamsKeyIds;
    /**
     * 监控链接结果上报接口参数名： 集数
     * 默认值：jishu
     */
    @Value("${config.sync.api.interface.feedback.params.key.jishu:jishu}")
    private String feedbackParamsKeyJishu;
    /**
     * 反馈接口获取反馈状态的JSON键名
     * 默认值：status
     */
    @Value("${config.sync.api.interface.feedback.json.key.status:status}")
    private String jsonKeyStatus;
    /**
     * 反馈接口获取反馈信息的JSON键名
     * 默认值：msg
     */
    @Value("${config.sync.api.interface.feedback.json.key.msg:msg}")
    private String jsonKeyMsg;
    @Autowired
    private YoutubeLinkService youtubeLinkService;
    @Autowired
    private RestTemplate restTemplate;
    private static YouTube youTube;

    static {
        youTube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, new HttpRequestInitializer() {
            public void initialize(HttpRequest request) throws IOException {
            }
        }).setApplicationName("monitor-youtube").build();
    }

    /**
     * 检测YouTube链接，通过YouTube API 获取视频信息，当成功获取到视频信息表示该视频正常。
     * @param youtubeLinks
     */
    public void check(List<YoutubeLink> youtubeLinks) {
        if (null == youtubeLinks || youtubeLinks.isEmpty()) {
            logger.debug("列表为空，返回！");
            return;
        }
        try {
            // 临时保存失败的列表
            List<YoutubeLink> failedList = new ArrayList<>();
            // 临时保存成功的列表
            List<YoutubeLink> queryList = new ArrayList<>();
            // 临时保存需要监控的链接ID
            List<String> queryIds = new ArrayList<>();
            /**
             * 1. 遍历视频链接列表，将VIDEO_ID为空的链接过滤掉。
             *    将待查询的链接保存至查询列表，同时生成查询链接ID列表。
             */
            for (YoutubeLink youtubeLink: youtubeLinks) {
                if (StringUtils.isBlank(youtubeLink.getVideoId())) { // VIDEO_ID为空
                    youtubeLink.setChecked(false);
                    youtubeLink.setCheckErrorTimes(youtubeLink.getCheckErrorTimes() + 1);
                    youtubeLink.setErrorMsg("YouTube链接视频编号为空！");
                    failedList.add(youtubeLink); // 不需要检测，直接添加到失败列表
                } else {
                    queryList.add(youtubeLink);
                    queryIds.add(youtubeLink.getVideoId());
                }
            }
            YouTube.Videos.List videosListMultipleIdsRequest = youTube.videos().list(youtubeVideoPart);
            videosListMultipleIdsRequest.setKey(youtubeApiKey);
            videosListMultipleIdsRequest.setId(queryIds.stream().collect(Collectors.joining(",")));
            VideoListResponse response = videosListMultipleIdsRequest.execute();
            logger.debug("【{}】 response: {}", videosListMultipleIdsRequest.getId(), response);

            List<Video> queryVideos = response.getItems();
            List<YoutubeLink> successList = new ArrayList<>();
            for (YoutubeLink youtubeLink: queryList) {
                if (isExistVideoId(youtubeLink.getVideoId(), queryVideos)) { // 存在指定节目反馈信息
                    youtubeLink.setChecked(true);
                    youtubeLink.setErrorMsg(null);
                    successList.add(youtubeLink);
                } else {
                    youtubeLink.setChecked(false);
                    youtubeLink.setCheckErrorTimes(youtubeLink.getCheckErrorTimes() + 1);
                    youtubeLink.setErrorMsg("VIDEO：【"+youtubeLink.getFeedbackId()
                            + ": " + youtubeLink.getVideoId()+"】无法获取到影片时长！");
                    failedList.add(youtubeLink);
                }
            }

            // 将检测结果保存至数据库
            for (YoutubeLink youtubeLink: successList) {
                this.youtubeLinkService.updateCheckResult(youtubeLink);
            }
            for (YoutubeLink youtubeLink: failedList) {
                /**
                 * 先更新失败信息。再从数据库记录中删除
                 */
                this.youtubeLinkService.updateCheckResult(youtubeLink);
                this.youtubeLinkService.delete(youtubeLink);
            }

            // 如果存在检测不通过的链接，则调用龙视反馈接口。
            if (!failedList.isEmpty()) {
                this.feedback(failedList);
            }
        }catch (Exception e) {
            logger.error("查询出现异常：", e);
        }
    }

    /**
     * 调用龙视上报接口。
     * @param list
     */
    private void feedback(List<YoutubeLink> list) {
        List<String> feedbackIds = new ArrayList<>();
        for (YoutubeLink youtubeLink: list) {
            logger.info("Feeback: 【{}: {} --> {}】{}",
                    youtubeLink.getId(), youtubeLink.getFeedbackId(), youtubeLink.getVideoId(),
                    youtubeLink.getErrorMsg());
            feedbackIds.add(youtubeLink.getFeedbackId());
        }


        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.put(feedbackParamsKeyIds, feedbackIds);
        String result = this.restTemplate.postForObject(syncApiServer + syncInterfaceFeedback,
                map, String.class);
        logger.debug("Interface: {}{} --> {}", syncApiServer, syncInterfaceFeedback, result);
        JSONObject jsonObject = JSON.parseObject(result);
        if (!jsonObject.getBoolean(jsonKeyStatus)) {
            logger.warn("反馈失败：{}", jsonObject.getString(jsonKeyMsg));
        }
    }

    private boolean isExistVideoId(String videoId, List<Video> list) {
        if (null == list || list.isEmpty() || StringUtils.isBlank(videoId)) {
            return false;
        }
        for (Video video: list) {
            if (videoId.equals(video.getId())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通过Youtube API请求，示例
     * @throws IOException
     */
    @Deprecated
    private void youtube() throws IOException {
        YouTube youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, new HttpRequestInitializer() {
            public void initialize(HttpRequest request) throws IOException {
            }
        }).setApplicationName("youtube-cmdline-search-sample").build();

        HashMap<String, String> parameters = new HashMap<>();
        parameters.put("part", youtubeVideoPart);
        parameters.put("id", "eIho2sssS0ZahI");

        YouTube.Videos.List videosListMultipleIdsRequest = youtube.videos().list(parameters.get("part").toString());
        videosListMultipleIdsRequest.setKey(youtubeApiKey);
        if (parameters.containsKey("id") && parameters.get("id") != "") {
            videosListMultipleIdsRequest.setId(parameters.get("id").toString());
        }

        VideoListResponse response = videosListMultipleIdsRequest.execute();


        System.out.println(response);
        for (Video video:response.getItems()) {
            System.out.println(video);
        }
    }

    /**
     * 直接通过HTTP请求, 示例
     * https://content.googleapis.com/youtube/v3/videos?id=Ks-_Mh1QhMc&part=snippet%2CcontentDetails%2Cstatistics&key=AIzaSyAMkHWnLNAvpKte-XA9nh3RheX7lFn_dNM
     * @throws IOException
     */
    @Deprecated
    private void youtubeHttp() throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建httpClient实例
        HttpGet httpGet = new HttpGet("https://content.googleapis.com/youtube/v3/videos?id=Ks-_Mh1QhMc&part=snippet%2CcontentDetails%2Cstatistics&key=AIzaSyAptYgyRCz6mTGvzWtmBbUQcsOOFkIFhJU"); // 创建httpget实例
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0"); // 设置请求头消息User-Agent
        CloseableHttpResponse response = httpClient.execute(httpGet); // 执行http get请求
        HttpEntity entity = response.getEntity(); // 获取返回实体
        System.out.println("网页内容：" + EntityUtils.toString(entity, "utf-8")); // 获取网页内容
        response.close(); // response关闭
        httpClient.close(); // httpClient关闭
    }
}
